package indi.zn.script.groovy;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;

import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException;
import groovy.util.ScriptException;

/**
 * 
 * 加载来源方式
 * 
 * 1 classpath groovy parse 
 * 2 sys file groovy 
 * 3 script groovy parse
 * 
 * 运行方式
 * 
 * groovyObject invikeMethod
 *  Java Interface compile
 *   Method shell eval
 * 
 * 默认上下文绑定  一个runner 一个上下文 ，runner 实例模式（单例，多例）有事使用环境自行控制
 * 
 * 
 * @author zn
 *
 */
public class GroovyRunner implements GroovyEvaluater,GroovyParser{

	private GroovyContext contextBinding;
	private CompilerConfiguration config;
	private GroovyScriptEngine engine;
	private GroovyClassLoader groovyloader;
	private GroovyShell gshell;
	
	private String[] basepath;
	
	
	public GroovyRunner() {
		this.contextBinding = new GroovyContext();
	    init(basepath);
		
	}
	
	public GroovyRunner(String[] basepath) {
		this.basepath=basepath;
		this.contextBinding = new GroovyContext();
	    init(basepath);
	}
	
	
	public GroovyRunner(String[] basepath, GroovyContext context) {

		this.basepath=basepath;
		this.contextBinding = context;
		init(basepath);
		
	}

	public GroovyRunner(String[] basepath, GroovyContext context, Properties prop) {
		// TODO Auto-generated constructor stub
		this.basepath=basepath;
		this.contextBinding = context;
		config = new CompilerConfiguration(prop);
		
		init(basepath);
	
	}

	void init(String[] basepath)  {

		try {
			this.engine = new GroovyScriptEngine(basepath);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.groovyloader = engine.getGroovyClassLoader();

		if (contextBinding != null && config != null) {
			gshell = new GroovyShell(contextBinding, config);
		} else {
			gshell = new GroovyShell(contextBinding);
		}

	}

	public GroovyContext getContext() {
		return contextBinding;
	}
	public CompilerConfiguration getConfig() {
		return config;
	}

	public GroovyScriptEngine getEngine() {
		return engine;
	}

	public GroovyClassLoader getGroovyloader() {
		return groovyloader;
	}

	public GroovyShell getGshell() {
		return gshell;
	}

	public String[] getBasepath() {
		return basepath;
	}

	public Object evaluate(String scrpit) {

		return gshell.evaluate(scrpit);

	}
	
	
	/**
	 * scrpit 上下文 独立 不受 runner 制约
	 * @param scrpit
	 * @param contextBinding
	 * @return
	 */
	public Object evaluate(String scrpit,Binding contextBinding) {

	  Script sc=gshell.parse(scrpit);
		
	  sc.setBinding(contextBinding);
		
	  return sc.run();

	}
	

	public Object evaluate(Reader in) {

		return gshell.evaluate(in);
	}

	public Class<?> parseGroovyClass(String groovyCode) {

		Class<?> clazz = groovyloader.parseClass(groovyCode);
		
		return clazz;

	}

	public Class<?> loadGroovyClass(String groovyName) {
		Class<?> clazz = null;
		try {
			clazz = engine.loadScriptByName(groovyName);
		} catch (ResourceException | ScriptException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return clazz;

	}

	public Class<?> parseGroovyClassByFile(File file) {
		Class<?> clazz = null;

		try {
			clazz = groovyloader.parseClass(file);
		} catch (CompilationFailedException | IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return clazz;

	}

	public GroovyObject parseToGroovyObject(String groovyCode,Object...comsparms) {

		Class<?> clazz = parseGroovyClass(groovyCode);

		GroovyObject go = getGroovyObjectInstance(clazz, comsparms);
		return go;
		
	}
	
	
	public Script parseToScript(String text){
		return this.gshell.parse(text);
	}
	
	public Script parseToScript(Reader reader){
		return gshell.parse(reader);
	}
	
	public Script parseToScript(File file){
		try {
			return gshell.parse(file);
		} catch (CompilationFailedException | IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public Script parseToScript(InputStream in){
		return gshell.parse(new InputStreamReader(in));
	}
	

	public Object invokeByGroovyFile(File file,Object[] consparms, String method, Object[] methodparms) {

		Class<?> clazz = parseGroovyClassByFile(file);

		GroovyObject go = getGroovyObjectInstance(clazz, consparms);

		return go.invokeMethod(method, methodparms);
	}

	public Object invokeByGroovyName(String groovyName,Object[] consparms, String method, Object[] methodparms) {

		Class<?> clazz = loadGroovyClass(groovyName);

		GroovyObject go = getGroovyObjectInstance(clazz,consparms);

		return go.invokeMethod(method, methodparms);
	}

	public Object invokeByGroovyCode(String groovyCode,Object[] consparms, String method, Object[] methodparms) {

		Class<?> clazz = parseGroovyClass(groovyCode);

		GroovyObject go = getGroovyObjectInstance(clazz,consparms);

		return go.invokeMethod(method, methodparms);
	}

	public Object invokeByClass(Class<?> clazz,Object[] consparms, String method, Object[] methodparms) {

		GroovyObject go = getGroovyObjectInstance(clazz, consparms);

		return go.invokeMethod(method, methodparms);

	}

	private GroovyObject getGroovyObjectInstance(Class<?> clazz,Object... consparms) {

		GroovyObject go = null;

		try {
			if (consparms == null) {
				go = (GroovyObject) clazz.newInstance();
			} else {

				Class<?>[] consTypes = new Class[consparms.length];

				for (int i = 0; i < consTypes.length; i++) {

					consTypes[i] = consparms[i].getClass();

				}
				go = (GroovyObject) clazz.getConstructor(consTypes).newInstance(consparms);
			}

		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
				| NoSuchMethodException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return go;

	}

}
